#ifndef YIGNETWORKCHANNELVIEW_H
#define YIGNETWORKCHANNELVIEW_H

// System includes
#include <QReadWriteLock>

// Local includes
#include "gui/yiggraphicsview.h"
#include "gui/yigsynthmenu.h"
#include "graphics/yigsynthgraphic.h"
#include "graphics/yigcablegraphic.h"
#include "interface/yigsynth.h"
//#include "graphics/yigglwidget.h"
#include "graphics/yigcursor.h"

/**
*   YigNetworkChannelView
*   View for an instance of a network channel. Handles SynthGraphic and CableGraphic Creation/Manipulation
*/
class YigNetworkChannelView : public QGraphicsView
{
    Q_OBJECT

public:

    /**
    *   YigNetworkChannelView constructor
    *   @param QWidget *parent Parent widget that contains this view
    */
    YigNetworkChannelView(QWidget* parent = 0);

    /**
    *   YigNetworkChannelView deconstructor, cleans up the synthGraphicMap and cableGraphicMap
    */
    ~YigNetworkChannelView();

    /**
    *   setActiveView sets this view as the main view for the local user.
    *   This decides whether or not the view can accept drop events and is interactive or read-only.
    *   @param bool Active status
    */
    void setActiveView(bool activeBool);

    /**
    *   getActiveBool Query the active status of this view
    *   @param bool Active status
    */
    bool getActiveBool();

    /**
    *   deletes all the synths, used for entering online mode. First we must delete everything then allow
    *   the networking to synchronize us
    */
    void clearSynths();

    QList<YigSynthGraphic*> getSynthGraphics();
    QList<YigCableGraphic*> getCableGraphics();

signals:

    /**
    *   dragAndDrop emitted when drag and drop processed has been completed
    *   triggers the new synth object creation process in YigNetworkChannel
    *   @param QString user name for the user creating the object
    *   @param QString the name of the synth object we're creating
    *   @param QPoint location of the new synth object
    *   @param float the randomized first parameter
    *   @param float the randomized second parameter
    */
    void dragAndDrop(QString userName, QString synthName, QPoint point, float param1, float param2);

    /**
    *   keyboardDeleteSynth Emitted when the local user attempts to delete a synth from the keyboard
    *   @param QString mapKey the key for the synth to be deleted
    */
    void keyboardDeleteSynth(QString mapKey);

    /**
    *   cableCreated emitted when the user has created a new cable between two synths
    *   @param QString mapKey The network channel view map key for this cable
    *   @param QString inputKey the map key for the synth that BEGINS the cable
    *   @param CableOutputType outputKey the map key for the synth that ENDS the cable
    *   @param QString outputType the type of output for the cable, either NO_OUTPUT, AUDIO, or MODULATION
    */
    void cableCreated(QString mapKey, QString inputKey, QString outputKey, YigCableGraphic::CableOutputType outputType);

    /**
    *   cableDestroyed emitted when the local user deletes a cable directly or indirectly
    *   QString mapKey the cableGraphicMap key, also mirrored in YigNetworkChannel
    */
    void cableDestroyed(QString mapKey);

    /**
    *   emitted when a parameter for a synth has changed
    *   @param QString mapKey The key for the synth that has changed
    *   @param YigSynthGraphic::ParamType The type of parameter (param1 or param2)
    *   @param float The new value for the parameter
    */
    void synthParamChanged(QString mapKey, YigSynthGraphic::ParamType paramType, float value);

    /**
    *   emitted when a synth is moved by the local user
    *   @param QString the map key for the synth that moved
    *   @param QPoint the new position
    */
    void synthPositionChanged(QString mapKey, QPointF point);

    /**
    *   emitted when the selected state of the synth changes locally
    *   @param QString the map key for the synth
    *   @param int The selected state bool represented as an int
    */
    void synthSelectedChanged(QString mapKey, int selected);

    /**
    *   cursorPositionChanged is emitted when the local user moves their mouse so that their cursor is updated over the network
    *   @param QPoint the new position
    */
    void cursorPositionChanged(QPoint point);

public slots:

    /**
    *   createSynthObect Create a new YigSynthGraphic and associate elements and store the synth graphic in the synthGraphicMap
    *   @param YigSynth* yigSynth Pointer to the YigSynth that was created. Used for Signal/Slot connection with the synth graphic
    *   @param QString key The key to use for map storage. This is important to keep the YigNetworkChannel and YigNetworkChannelView maps mirrored.
    *   @param QString synthName Name of the type of synth object to creat (sin,square, etc...)
    *   @param QPoint point Where in the scene the synth object should be created
    *   @param float the randomized first parameter
    *   @param float the randomized second parameter
    */
    void createSynthGraphic(YigSynth* yigSynth, QString key, QString synthName, QPoint point, float param1, float param2);

    /**
    *   destroySynthGraphic Destroy a synth graphic in this view
    *   @param QString key The key for the synth graphic to be removed/deleted
    */
    void destroySynthGraphic(QString key);

    /**
    *   updates a synth position from the network
    *   @param QString the map key of the synth to update
    *   @param int The new x position of the synth
    */
    void updateSynthPositionX(QString mapKey, int xPos);

    /**
    *   updates a synth position from the network
    *   @param QString the map key of the synth to update
    *   @param int The new y position of the synth
    */
    void updateSynthPositionY(QString mapKey, int yPos);

    /**
    *   updates a synth parameter modulation from the network
    *   @param const char* The map key for the synth
    *   @param float the new value of the parameter
    */
    void updateSynthParam1(QString mapKey, float param1);

    /**
    *   updates a synth parameter modulation from the network
    *   @param const char* The map key for the synth
    *   @param float the new value of the parameter
    */
    void updateSynthParam2(QString mapKey, float param2);

    /**
    *   called when the selected state of the synth changes locally
    *   @param QString the map key for the synth
    *   @param QString the userName of the user that has selected or unselected the synth
    */
    void updateSynthSelected(QString mapKey, QString userName);

    /**
    *   calls autoCable() on all synths in our current map. Used to update the current connections after startup
    */
    void autoCableAllSynths();

    /**
    *   createCable Create a cable in this view
    *   @param QString inputKey The synthGraphicMap key for the input synth
    *   @param QString outputKey The synthGraphicMap key for the output synth
    *   @param YigCableGraphic::CableOutputType outputType  The output type of the cable AUDIO or MODULATION
    */
    void createCableGraphic(QString inputKey, QString outputKey, YigCableGraphic::CableOutputType outputType);

    /**
    *   destroyCableGraphic Destroy a cable graphic in this view
    *   @param QString key The key for the cable graphic to be removed/deleted
    */
    void destroyCableGraphic(QString mapKey);


    /**
    *   checkEllipseClick called when the local user clicks on an audioOutput ellipse
    *   @param QString mapKey the map key for the synth graphic
    *   @param EllipseType the type of ellipse, either AUDIO_INPUT, AUDIO_OUTPUT, or MOD_INPUT
    *   @param const QPointF& point the CENTER of the output ellipse
    */
    //void checkEllipseClick(QString mapKey, YigSynthGraphic::EllipseType ellipseType, QPointF point);

    /**
    *   addUser called when a new user logs on to the network. We construct a new cursor for the user and add it to the cursorGraphicMap
    *   @param QString The username of the user to add.
    */
    void addUser(QString userName);

    /**
    *   removeUser called when a user logs off the network, so we remove it from the cursorGraphicMap
    *   @param QString The username of the user to remove
    */
    void removeUser(QString userName);

    /**
    *   updates a cursor position from the network
    *   @param QString the map key of the cursor to update
    *   @param int The new x position of the cursor
    */
    void updateCursorPositionX(QString userName, int xPos);

    /**
    *   updates a cursor position from the network
    *   @param QString the map key of the cursor to update
    *   @param int The new y position of the cursor
    */
    void updateCursorPositionY(QString userName, int yPos);

protected:

    /** overridden to redraw our background on the event of a resize */
    void resizeEvent(QResizeEvent *event);

    /**
    *   dragMoveEvent Called when drag is moved, right now does nothing, perhaps draws a preview of the object in the future?
    *   @param QDragMoveEvent *event the particular drag move event for this action
    */
    void dragMoveEvent(QDragMoveEvent *event);

    /**
    *   dropEvent Call on drop, we use to emit dragAndDrop(QString)
    *   @param QDropEvent *event that particular drop event for this action
    */
    void dropEvent(QDropEvent *event);

    /**
    *   keyPressEvent Call on on key press, passed on to parent classes, overridden for synth deletion
    *   @param QKeyEvent *event the particular key event
    */
    void keyPressEvent(QKeyEvent *event);

    /**
    *   mouseMoveEvent Overridden to create selection rect
    *   @param QMouseEvent *event;
    */
    void mouseMoveEvent(QMouseEvent *event);

    /**
    *   mousePressEvent start selection rect
    *   @param QMouseEvent *event
    */
    void mousePressEvent(QMouseEvent *event);

    /**
    *   mouseReleaseEvent destroys selectionRect
    *   @param QMouseEvent *event
    */
    void mouseReleaseEvent(QMouseEvent *event);

    /** overridden to implement zooming */
    void wheelEvent(QWheelEvent *event);


private:

    /**
    *   createCurrentCable Creates the current cable and begins mouse tracking for segment additions
    *   @param QString beginSynthKey mapKey for the synth that begins the cable
    *   @param QPoint point The point in the view where the cable begins
    */
    //void createCurrentCable(QString beginSynthKey, QPoint point);

    /**
    *   finishCurrentCable Finishes the current cable and ends mouse tracking for segment additions
    *   @param QString endSynthKey mapKey for the synth that ends the cable
    *   @param QPoint point The point in the view where the cable ends
    *   @param YigCableGraphic::CableOutputType outputType The type of cable (audio/mod) this cable outputs as
    */
    //void finishCurrentCable(QString endSynthKey, QPoint point, YigCableGraphic::CableOutputType outputType);

    /**
    *   addCable Adds a cable to the view/scene
    *   @param QString the map key for the new cable
    *   @param YigCableGraphic* cable Pointer to the new cable. The view takes ownership of the cable and will delete it in the deconstructor
    */
    void addCable(QString mapKey, YigCableGraphic* cable);

    /**
    *   removeCurrentCable Called from various actions when the currentCable needs to be removed from the view/scene
    */
    //void removeCurrentCable();

    /** remake the selectedSynthKey list */
    //void makeSelectedSynthKeyList();

    /** Boolean for the active state of this view */
    bool mActiveBool;

    bool mouseDownBool;

    /** Map of the Cables in this network channel */
    QHash<QString,YigCableGraphic*> cableGraphicMap;

    /** List of deleted graphics, used to prevent memory leaks from synth->cable deletion and keyboard->cable deletion */
    //QList<QGraphicsItem*> deletedGraphics;

    /** The possible current cable which follows the mouse until assigned to a second synth */
    YigCableGraphic* currentCable;

    /** Flag that describes whether there is a cable currently being created/edited by the local user or not */
    bool currentCableBool;

    /** counter for cables to ensure unique map keys */
    int cableCounter;

    /** list of the network channel map keys of the current selected synths */
    QList<QString> selectedSynthKeys;

    /** parent of audio cables, enables them to always be stacked above mod cables */
    QGraphicsLineItem* audioCableParent;
    /** parent of mod cables, enables them to always be stacked below audio cables */
    QGraphicsLineItem* modCableParent;
    /** map of the cursors for the users */
    QHash<QString,YigCursor*> cursorGraphicMap;
    /** Map of Synths in this network channel */
    QHash<QString,YigSynthGraphic*> synthGraphicMap;
    /** ensures thread safe access to resources */
    QReadWriteLock lock;
    /** mouse counter counts to a certain amount before we can emit a mouse position update */
    int mouseCounter;
    /** used to anchor the selectionRect */
    QPoint mouseDownPos;
    /** used to size the selectionRect */
    QPoint lastMouseViewPos;

    /** the selectionRect shape is used to check for items in the graphics view and select
    *  those that fall within the bounds of the selectionRect.
    */
    QGraphicsRectItem* selectionRect;

    /** the current scale factor of the view */
    qreal scaleFactor;
};

#endif // YIGNETWORKCHANNELVIEW_H
